<?php

namespace Core\Common\Util;

use Core\Common\Route\Request;

class Helper
{

    /**
     * <p>msgpack压缩数据
     * @param mixed $data
     * @return mixed
     */
    public static function encode($data, $dataType = 'msgpack')
    {
        if ($dataType == 'json') {
            return json_encode($data, JSON_PARTIAL_OUTPUT_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        }
        return msgpack_pack($data);
    }

    /**
     * <p>msgpack解压数据
     * @param mixed $str
     * @return mixed
     */
    public static function decode($str, $dataType = 'msgpack')
    {
        if (!is_string($str)) {
            return $str;
        }
        if ($dataType == 'json') {
            return json_decode($str, true);
        }
        return msgpack_unpack($str);
    }

    public static function deflate($data)
    {
        return gzdeflate($data, 9);
    }

    public static function inflate($data)
    {
        return gzinflate($data);
    }

    public static function array2Bytes(array $data)
    {
        array_unshift($data, 'S*');
        $str = call_user_func_array('pack', $data);
        return $str;
    }

    public static function bytes2Array($data)
    {
        $ret = unpack('S*', $data);
        return array_values($ret);
    }

    /**
     * 获得num个比特位全部为1的掩码
     * @param int $num
     * @return int
     */
    public static function bitmask($num)
    {
        $ret = 0;
        while ($num) {
            $ret |= (1 << ($num - 1));
            $num--;
        }
        return $ret;
    }

    /**
     * 把数组$args中每个元素从左到右，按顺序把每个数字按它所占的bit位数，编码到一个数字里
     * @return int
     */
    public static function encode2Bit($args, $bits)
    {
        $ret = 0;
        foreach ($bits as $i => $num) {
            if ($i) {
                $ret <<= $bits[$i];
            }
            $ret |= ((int)array_shift($args)) & self::bitmask($num);
        }
        return $ret;
    }

    /**
     * 把数字按配置的bit位数的要求，分解成数组
     * @param int $input
     * @return array
     */
    public static function decodeFromBit($input, $bits)
    {
        $ret = array();
        $tmp = $bits;
        while ($tmp) {
            $num = array_pop($tmp);
            array_unshift($ret, $input & self::bitmask($num));
            $input >>= $num;
        }
        return $ret;
    }

    /**
     * <p>数据压缩
     * @param type $data
     * @param type $level
     * @return type
     */
    public static function compress($data, $level = 9)
    {
        return gzdeflate(json_encode($data), $level);
    }

    /**
     * <p>数据解压
     */
    public static function uncompress($data)
    {
        $ret = json_decode(gzinflate($data), true);
        return $ret;
    }

    /**
     * <p>四舍五入, 两位小数
     * @param float $val
     * @return float
     */
    public static function format($val)
    {
        return round($val, 2, PHP_ROUND_HALF_UP);
    }

    /**
     * 带毫秒数的日期
     * @return string
     */
    public static function date($format = 'Y-m-d H:i:s')
    {
        list($m1,) = self::explode(' ', microtime());
        return date($format) . substr($m1, 1, 5);
    }

    public static function var_dump(...$data)
    {
        if (PHP_SAPI == 'cli') {
            var_dump(...$data);
        } else {
            echo "<pre>";
            var_dump(...$data);
            echo "</pre>";
        }
    }

    /**
     * 按fields的数量array_combile，避免出现警告
     * @return array
     */
    public static function array_combine($fields, $values)
    {
        if (count($fields) == count($values)) {
            return array_combine($fields, $values);
        }
        $ret = array();
        foreach ($fields as $key) {
            $ret[$key] = array_shift($values);
        }
        return $ret;
    }

    public static function array_merge(...$param)
    {
        if (count($param) == 1) {
            return (array)$param[0];
        }
        $tmp = array();
        foreach ($param as $arr) {
            if (empty($arr)) {
                continue;
            }
            $tmp[] = (array)$arr;
        }
        return call_user_func_array('array_merge', $tmp);
    }

    public static function ratioRadom(array $arr)
    {
        $sum = array_sum($arr);
        $rand = mt_rand(0, $sum - 1);
        $i = 0;
        foreach ($arr as $k => $v) {
            $i += $v;
            if ($i >= $rand) {
                return $k;
            }
        }
        return 0;
    }

    /**
     * <p>分割字符串
     * @param string $delimeter
     * @param string $str
     * @return array
     */
    public static function explode($delimeter, $str, $limit = null)
    {
        if (is_array($str)) {
            return $str;
        }
        $str = trim($str);
        if ($str) {
            if (is_null($limit)) {
                return explode($delimeter, $str);
            }
            return explode($delimeter, $str, $limit);
        }
        return array();
    }

    public static function rowExplode($delimeter, $str, $limit = PHP_INT_MAX)
    {
        if (is_array($str)) {
            return $str;
        }
        if (!$str) {
            return array();
        }
        if (!is_array($delimeter)) {
            return self::explode($delimeter, $str, $limit);
        }
        $first = array_shift($delimeter);
        $tmp = self::explode($first, $str, $limit);
        $second = array_shift($delimeter);
        if (!$second) {
            return $tmp;
        }
        $ret = array();
        foreach ($tmp as $line) {
            $ret[] = self::explode($second, $line, $limit);
        }
        return $ret;
    }

    public static function parseConfig($str, $delimeter = ',', $delimeter2 = ':')
    {
        $ret = array();
        $tmp = self::explode($delimeter, $str);
        foreach ($tmp as $line) {
            list($k, $v) = self::explode($delimeter2, $line);
            $ret[(int)$k] = (int)$v;
        }
        return $ret;
    }

    /**
     * 正则分割字符串
     * @param type $delimeter
     * @param type $str
     * @return type
     */
    public static function split($delimeter, $str)
    {
        if ($str) {
            return preg_split('#' . $delimeter . '#', trim($str));
        }
        return array();
    }

    public static function interLeave($x, $y)
    {
        $x0 = sprintf('%09b', $x);
        $y0 = sprintf('%09b', $y);
        $ret = array();
        for ($i = 0; $i < 9; ++$i) {
            array_push($ret, (int)$x0[$i]);
            array_push($ret, (int)$y0[$i]);
        }
        return implode('', $ret);
    }

    public static function calTime($class, $line)
    {
        static $total = 0;
        static $time = null;
        if (is_null($time)) {
            $time = microtime(true);
            echo $class . ':' . $line . ': 0' . "\n";
            return;
        }
        $now = microtime(true);
        $t = 1000 * ($now - $time);
        $time = $now;
        $total += $t;
        echo $class . ':' . $line . ': ' . self::format($t) . ' ' . self::format($total) . "\n";
    }
    //marchinfo.unit[0].id
    //marchinfo.unit[0].num
    public static function parseString(&$result, $param, $value)
    {
        $arr = explode('.', $param);
        $obj = &$result;
        foreach ($arr as $val) {
            if (false !== ($pos = strpos($val, '['))) {
                $name = substr($val, 0, $pos);
                $endPos = strpos($val, ']');
                $index = substr($val, $pos + 1, $endPos - $pos - 1);
                if (!isset($obj[$name])) {
                    $obj[$name] = array();
                }
                if (!isset($obj[$name][$index])) {
                    $obj[$name][$index] = array();
                }
                $obj = &$obj[$name][$index];
            } else {
                if (!isset($obj[$val])) {
                    $obj[$val] = '';
                }
                $obj = &$obj[$val];
            }
        }
        $obj = $value;
    }

    /**
     * 递归检查两个数组的不同之处
     * @param type $first
     * @param type $second
     * @param type $from 输出的key值前缀
     * @return type
     */
    public static function diffArray($first, $second, $from = '')
    {
        $str = '';
        if (is_null($second)) {
            $str .= "param $from is null.\n";
            return $str;
        }
        $index = empty($from) ? '' : $from . '.';
        foreach ($first as $key => $value) {
            if (!isset($second[$key])) {
                $val = var_export($value, true);
                $str .= "miss key: {$index}{$key}={$val}.\n";
                continue;
            }
            if (is_array($value)) {
                $str .= self::diffArray($value, $second[$key], $index . $key);
            } else {
                if ($value !== $second[$key]) {
                    $str .= "{$index}{$key}, {$value} !== {$second[$key]}\n";
                }
            }
        }
        return $str;
    }

    public static function getIpAddress()
    {
        $bash = '';
        if ('Linux' == PHP_OS) {
            $bash = "if [ -e /usr/sbin/ip ];then /usr/sbin/ip -4 addr | grep inet | grep -v inet6 | grep -v '127.0.0.1' | awk '{print $2}' | cut -f 1 -d /; "
                    ."elif [ -e /sbin/ip ];then /sbin/ip -4 addr | grep inet | grep -v inet6 | grep -v '127.0.0.1' | awk '{print $2}' | cut -f 1 -d /;"
                    . "elif [ -e /usr/sbin/ifconfig ];then /usr/sbin/ifconfig | grep inet | grep -v inet6 | awk '{print $2}' | grep -v '127.0.0.1'; "
                    . "elif [ -e /sbin/ifconfig ];then /sbin/ifconfig | grep inet | grep -v inet6 | awk '{print $2}' | grep -v '127.0.0.1'; fi";
        } else if ('Darwin' == PHP_OS) {
            $bash = "/sbin/ifconfig | grep inet | grep -v inet6 | awk '{print $2}' | grep -v '127.0.0.1'";
        }
        $data = [];
        exec($bash, $data);
        foreach ($data as $str) {
            $line = str_replace('addr:', '', $str);
            list($a, $b, ) = explode('.', $line);
            if ('127' == $a) {
                continue;
            }
            if ('10' == $a ) {
                return $line;
            }
            if ('172' == $a && (16 <= $b || 31 >= $b)) {
                return $line;
            }
            if ('192' == $a && '168' == $b ) {
                return $line;
            }
        }
        return '';
    }

    public static function isInnerIpAddress($line)
    {
        list($a, $b, ) = explode('.', $line);
        if ('127' == $a) {
            return true;
        }
        if ('10' == $a ) {
            return true;
        }
        if ('172' == $a && (16 <= $b || 31 >= $b)) {
            return true;
        }
        if ('192' == $a && '168' == $b ) {
            return true;
        }
        return false;
    }

    public static function getClientIpAdress()
    {
        if (Request::server('HTTP_CLIENT_IP')) {
            return Request::server('HTTP_CLIENT_IP');
        }
        if (Request::server('HTTP_X_FORWARDED_FOR')) {
            return explode(',', Request::server('HTTP_X_FORWARDED_FOR'))[0];
        }
        if (Request::server('X_FORWARDED_FOR')) {
            return explode(',', Request::server('X_FORWARDED_FOR'))[0];
        }
        if (Request::server('REMOTE_ADDR')) {
            return Request::server('REMOTE_ADDR');
        }
        if (Request::server('HTTP_X_REAL_IP')) {
            return Request::server('HTTP_X_REAL_IP');
        }
        if (Request::server('X_REAL_IP')) {
            return Request::server('X_REAL_IP');
        }
        return 'Unknown';
    }

    public static function getRemoteIpAdress()
    {
        if (Request::server('REMOTE_ADDR')) {
            return Request::server('REMOTE_ADDR');
        }
        return 'Unknown';
    }

    public static function getMillisecond()
    {
        list($s1, $s2) = explode(' ', microtime());
        return (float)sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000);
    }

    public static function getHostName()
    {
        static $hostName = '';
        if (empty($hostName)) {
            $hostName = gethostname();
        }
        return $hostName;
    }


    public static function getCurrentUser()
    {
        static $username = '';
        if (empty($username)) {
            $username = get_current_user();
        }
        return $username;
    }
    /**
     * 本周第一个时间(秒)
     * @param type $timestamp
     * @return type
     */
    public static function weekFirstTime($timestamp = true)
    {
        $date = date('Y-m-d 00:00:00', strtotime('-'.(date('N') - 1).'days') - 1);
        if ($timestamp) {
            return strtotime($date);
        }
        return $date;
    }
    /**
     * 本周最后一个时间(秒)
     * @param type $timestamp
     * @return type
     */
    public static function weekLastTime($timestamp = true)
    {
        $date = date('Y-m-d 23:59:59', strtotime((7 - date('N') ).'days') );
        if ($timestamp) {
            return strtotime($date);
        }
        return $date;
    }
    
    /**
     * 上周第一个时间(秒)
     * @param type $timestamp
     * @return type
     */
    public static function lastWeekFirstTime($timestamp = true)
    {
        $date = date('Y-m-d 00:00:00', strtotime('-1 week -'.(date('N') - 1).'days') - 1);
        if ($timestamp) {
            return strtotime($date);
        }
        return $date;
    }
    /**
     * 上周最后一个时间(秒)
     * @param type $timestamp
     * @return type
     */
    public static function lastWeekLastTime($timestamp = true)
    {
        $date = date('Y-m-d 23:59:59', strtotime('-'.(date('N') ).'days') );
        if ($timestamp) {
            return strtotime($date);
        }
        return $date;
    }
    /**
     * 上月第一个时间(秒)
     * @return type
     */
    public static function lastMonthFirstTime($timestamp = true)
    {
        $date = date('Y-m-d 00:00:00', strtotime('-1 month -'.(date('j') - 1).'days') - 1);
        if ($timestamp) {
            return strtotime($date);
        }
        return $date;
    }
    /**
     * 上月最后一个时间(秒)
     * @param type $timestamp
     * @return type
     */
    public static function lastMonthLastTime($timestamp = true)
    {
        $date = date('Y-m-d 23:59:59', strtotime('-'.(date('j') ).'days') );
        if ($timestamp) {
            return strtotime($date);
        }
        return $date;
    }
}